import os
import traceback
from threading import Thread
from typing import Callable, List, Dict

from scripts.Log import Log
from scripts.device.AdbShell import AdbShell, PmListPkgInfo


class Util(object):
    BACKUP_DONE_FILE = '.done'

    @staticmethod
    def exit_on_exception(callback: Callable[[], None]):
        try:
            callback()
        except BaseException:
            msg = traceback.format_exc()
            Log.e(msg)
            Util.exit(1)

    @staticmethod
    def ignore_exception(callback: Callable[[], None]):
        try:
            callback()
        except BaseException:
            msg = traceback.format_exc()
            Log.e(msg)

    @staticmethod
    def async_task(callback: Callable[[], None]):
        Log.d('=========async_task() begin==========')
        Thread(target=lambda: Util.exit_on_exception(callback)).start()
        Log.d('=========async_task() end==========')

    @staticmethod
    def exit(code: int):
        os._exit(code)

    @staticmethod
    def is_backup_done(backup_path: str):
        backup_done_path = os.path.join(backup_path, Util.BACKUP_DONE_FILE)
        return os.path.exists(backup_done_path)

    @staticmethod
    def mark_backup_done(backup_path: str):
        backup_done_path = os.path.join(backup_path, Util.BACKUP_DONE_FILE)
        with open(backup_done_path, 'w', encoding='utf8'):
            pass

    @staticmethod
    def rmdirs(dir_path: str):
        for root, dirs, files in os.walk(dir_path, topdown=False):
            for name in files:
                os.remove(os.path.join(root, name))
            for name in dirs:
                os.rmdir(os.path.join(root, name))

    @staticmethod
    def create_device_dirs(host_path: str, device_path: str, shell: AdbShell, empty_only: bool = False):
        if not os.path.isdir(host_path):
            return

        filenames = os.listdir(host_path)

        # create
        if not empty_only or len(filenames) == 0:
            Log.d('create dir [%s]' % device_path)
            shell.mkdir(device_path)

        # for sub folders
        for filename in filenames:
            Util.create_device_dirs(host_path=os.path.join(host_path, filename),
                                    device_path='%s/%s' % (device_path, filename),
                                    shell=shell,
                                    empty_only=empty_only)

    @staticmethod
    def find_host_files(path: str) -> List[str]:
        if os.path.isdir(path):
            file_list = []
            files = os.listdir(path)
            for file in files:
                file_list += Util.find_host_files(os.path.join(path, file))
            return file_list
        else:
            return [path, ]

    @staticmethod
    def dump_pkg_infos(shell: AdbShell) -> Dict[int, Dict[str, PmListPkgInfo]]:
        pkgs: Dict[int, Dict[str, PmListPkgInfo]] = dict()

        user_infos = shell.pm_list_users()

        for user_info in user_infos:
            pkgs[user_info.user_id] = {item.pkg: item for item in shell.pm_list_packages(user_info.user_id)}

        return pkgs

    class FileExistsHelper(object):
        def __init__(self, shell: AdbShell):
            self._shell = shell
            self._status: Dict[str, bool] = dict()

        def exists(self, path: str) -> bool:
            if path not in self._status:
                self._status[path] = self._shell.path_exists(path)
            return self._status[path]
